function res = setDemodPhase(obj, varargin)
%SETDEMODPHASE Summary of this function goes here
%   Detailed explanation goes here
    p=inputParser;
    p.addParameter('isPlot', false, @islogical);
    p.parse(varargin{:});
    
    %%
    sess = obj.session;
    zi=sess.getDevice(obj.master);
    
    %%
    obj.setZ1CoilOffset(0.0).disableZ1CoilAmplitude().enableZ1CoilModulation();
    obj.enableX5CoilModulation();
    
    obj.setSingal_DemodPhase('0', 0.0);
    obj.setSignal_By().setSignal_Bz(); % using default filter order, bw, etc.
    
    %% Setting: Y
    demod_chY = '2'; bias_startY = 0.0; bias_stopY = 0.3;
    scan_chZ = '3'; scan_valZ = 0:0.025:0.05; 
    [phaseResultY, biasResultY] = findPhase(sess, zi, demod_chY, scan_chZ, scan_valZ, bias_startY, bias_stopY);

    %% Setting: Z
    demod_chZ = '3'; bias_startZ = 0.0; bias_stopZ = 0.2;
    scan_chY = '2'; scan_valY = 0:0.025:0.05; 
    [phaseResultZ, biasResultZ] = findPhase(sess, zi, demod_chZ, scan_chY, scan_valY, bias_startZ, bias_stopZ);

    %% output
    zi.setDemodPhase(demod_chY, phaseResultY.optPhase);
    zi.setAuxOutOffset(demod_chY, biasResultY.optBias);
    
    zi.setDemodPhase(demod_chZ, phaseResultZ.optPhase);
    zi.setAuxOutOffset(demod_chZ, biasResultZ.optBias);

    %% summary    
    res.optPhaseY = phaseResultY.optPhase; 
    res.optPhaseZ = phaseResultZ.optPhase; 
    res.optBiasY = biasResultY.optBias;
    res.optBiasZ = biasResultZ.optBias;
    
    res.phaseY = phaseResultY;
    res.phaseZ = phaseResultZ;
    res.biasY = biasResultY;
    res.biasZ = biasResultZ;
    
    %% plot
    if p.Results.isPlot
        EquipmentControl.ZI.plot_demodPhase(phaseResultY, biasResultY);
        EquipmentControl.ZI.plot_demodPhase(phaseResultZ, biasResultZ);
    end
end

function [phaseResult, biasResult] = findPhase(session, zi, demod_ch, scan_ch, scan_val, bias_start, bias_stop)
%SETDEMODPHASE �˴���ʾ�йش˺�����ժҪ
%   �˴���ʾ��ϸ˵��

    devName = zi.device;
    sweeper = zi.sweepModule(session);

    %% Scan Phase
    sweeper.setControl('gridnode', ['demods/' demod_ch '/phaseshift'], ...
                       'xmapping', 0, 'start', 0, 'stop',180, 'samplecount', 33)...
           .setFilter('bandwidthcontrol', 0)...
           .setSettling('settling_tc', 0.1)...
           .setStatistics('averaging_sample', 10)...
           .setOption();%.showParam();
%     sweeper.load_settings([path_to_setting_file 'settings.mat'], 'swpr_settings');

    loop_name = ['Ch' num2str(scan_ch) 'ScanAmp'];
    session.addParameter('ScanVoltChannel', num2str(scan_ch));
    session.addParameter('DemodChannel', num2str(demod_ch));
    session.addParameter('ScanLoopName', loop_name);
    session.addParameter('ScanAmp', scan_val, 'V');
    session.makeLoopParameter(loop_name, 'ScanAmp');
    phaseScanResult = sweeper.batchRunSession(loop_name, @batch_fun);
    session.addMeasurementData(loop_name, phaseScanResult);

    phaseResult = getOptimalPhase(phaseScanResult, devName, demod_ch);
    zi.setDemodPhase(demod_ch, phaseResult.optPhase);

    %% Scan Bias
    sweeper.setControl('gridnode', ['auxouts/' demod_ch '/offset'], ...
                       'xmapping', 0, 'start', bias_start, 'stop',bias_stop, 'samplecount', 33)...
           .setFilter('bandwidthcontrol', 0)...
           .setSettling('settling_tc', 0.1)...
           .setStatistics('averaging_sample', 10)...
           .setOption();%.showParam();

    sweeper.subscribeChannel(demod_ch);
    biasScanResult = sweeper.run();
    sweeper.unsubscribeChannel();     

    biasResult = getOptimalBias(biasScanResult, devName, demod_ch);
    zi.setAuxOutOffset(demod_ch, biasResult.optBias);

end

%% Operations of Each Sweep
function res = batch_fun(session, module, index, varargin)
    %  get Parameters
    p=inputParser;
    isModule = @(x) isa(x, 'EquipmentControl.ZI.ziModule.ziModule');
    isSession = @(x) isa(x, 'MeasurementSession.AbstractMeasurementSession');
    
    p.addRequired('session', isSession);
    p.addRequired('module', isModule );
    p.addRequired('index', @isnumeric);
    p.addParameter('processMonitor', @module.plotData);
    p.parse(session, module, index, varargin{:});

    sess = p.Results.session;
    swper = p.Results.module;
    idx = p.Results.index;
    updataPlot = p.Results.processMonitor;
    
    zi = sess.getDevice(swper.device);

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %  pre_acquisition operations
    loop_name = session.getParameter('ScanLoopName');
    volt = sess.getLoopParameter(loop_name, idx);
    scanChStr = sess.getParameter('ScanVoltChannel');
    demodChStr = sess.getParameter('DemodChannel');
    
    fprintf('BatchRun at paramter %s(index = %d): %f ... \n', loop_name, idx, ...
            sess.getLoopParameter(loop_name, idx) );    
    zi.setAuxOutOffset(scanChStr, volt );
    pause(0.5);
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % data acquisition
    swper.setMonitorNode('x');
    swper.subscribeChannel(demodChStr);
    res = swper.run('index', idx, 'silence', 0, 'processMonitor', updataPlot);
    swper.unsubscribeChannel();
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % post_acquisition
    pause(0.5);
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
end

%%
function res = getOptimalPhase(scanResult, DEVICE_NAME, idxStr)
    ch = str2double(idxStr) + 1;
    res.phaseList = scanResult{1}.(DEVICE_NAME).demods(ch).sample{1}.grid;
    res.data = zeros(length(scanResult), length(res.phaseList));
    for k=1:length(scanResult)
        res.data(k,:) = scanResult{k}.(DEVICE_NAME).demods(ch).sample{1}.x;
    end    
    [res.minVar, res.minIdx] = min(var(res.data));
    res.optPhase = res.phaseList(res.minIdx);    
end

function res = getOptimalBias(biasScanResult, DEVICE_NAME, idxStr)
    ch = str2double(idxStr) + 1;
    bias = biasScanResult.(DEVICE_NAME).demods(ch).sample{1}.grid;
    val = biasScanResult.(DEVICE_NAME).demods(ch).sample{1}.x;
    res.fitRes = fit(bias.', val.', 'poly1');
    res.optBias = - res.fitRes.p2/res.fitRes.p1;
    res.bias = bias;
    res.data = val;
end

